
package Database;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Vector;


class ConnectionReaper extends Thread
{
    private DBConnectionPool pool;

    // 300 Seconds
    private final long delay = 300000;

    ConnectionReaper( DBConnectionPool pool )
    {
        this.pool = pool;
    }

    @Override
    public void run()
    {
        // Run Forever
        while ( true )
        {
            try
            {
                // Rest
                sleep( delay );
            }
            catch ( InterruptedException e )
            {
            }
            
            // To Resolve Deadlocks ...
            pool.reapConnections();
        }
    }
}

public class DBConnectionPool
{
    private Vector<DBConnection> connections ;

    private String url , user , password ;

    final private long timeout = 60000 ;

    private ConnectionReaper reaper ;

    final private int poolSize = 100 ;

    public DBConnectionPool( String url , String user , String password )
    {
        this.url = url;
        this.user = user;
        this.password = password;
        
        connections = new Vector<DBConnection>( poolSize );
        
        reaper = new ConnectionReaper( this );
        reaper.start();
    }

    public synchronized void reapConnections()
    {
        long stale = System.currentTimeMillis() - timeout;
     
        for( int i = 0 ; i < connections.size() ; i ++ )
        {
            DBConnection connection = (DBConnection) connections.get( i );

            if ( (connection.inUse()) && (stale > connection.getLastUse()) && (!connection.validate()) )
            {
                removeConnection( connection );
            }
        }
    }

    public synchronized void closeConnections()
    {
        for( int i = 0 ; i < connections.size() ; i ++ )
        {
            DBConnection connection = (DBConnection) connections.get( i );
            
            removeConnection( connection );
        }
    }

    private synchronized void removeConnection( DBConnection connection )
    {
        connections.removeElement( connection );
    }

    public synchronized Connection getConnection() throws SQLException
    {
        DBConnection jdcConnection;
     
        for ( int i = 0 ; i < connections.size() ; i++ )
        {
            jdcConnection = (DBConnection) connections.elementAt( i );
            
            if ( jdcConnection.lease() )
            {
                return jdcConnection;
            }
        }

        Connection connection = DriverManager.getConnection( url , user , password );
        
        jdcConnection = new DBConnection( connection , this  );
        jdcConnection.lease();
        
        connections.addElement( jdcConnection );
        
        return jdcConnection;
    }

    public synchronized void returnConnection( DBConnection connection )
    {
        connection.expireLease();
    }
}
